using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Space
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class CameraComponent : Microsoft.Xna.Framework.GameComponent, ICamera
    {
        Vector3 position; public Vector3 Position { get { return position; }
            set { position = value; }
        }
        Vector3 target; public Vector3 Target { get { return target; } set { target = value; } }
        Vector3 up; public Vector3 Up { get { return up; } set { up = value; } }

        Matrix view;
        Matrix proj;

        public Matrix View { get { return view; } }
        public Matrix Projection { get { return proj; } }

        public CameraComponent(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
            Game.Services.AddService(typeof(ICamera), this);
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), Game.GraphicsDevice.Viewport.AspectRatio, 10, 500, out proj);

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            GetView(out view);

            base.Update(gameTime);
        }

        public void GetView(out Matrix m)
        {
            Matrix.CreateLookAt(ref position, ref target, ref up, out m);
        }

        public void Translate(Vector3 v)
        {
            position += v;
        }

        public void Zoom(ref Vector2 center, float distance, Viewport viewport)
        {
            Vector3 lookingDirection = target - position;

            Vector3 v1 = viewport.Unproject(new Vector3(center, 0), proj, view, Matrix.Identity);
            Vector3 v2 = viewport.Unproject(new Vector3(center, 1), proj, view, Matrix.Identity);
            Ray ray = new Ray(v1, Vector3.Normalize(v2 - v1));
            Vector3 newPos = position + ray.Direction * distance;

            float minDist = 15;

            if (newPos.Y < minDist)
            {
                if (ray.Direction.Y == 0) return;
                newPos -= ray.Direction * ((minDist - newPos.Y) / -ray.Direction.Y);
            }
            position = newPos;

            target = position + lookingDirection;
        }

        public Vector2 Project(Vector3 position)
        {
            Vector3 v = Game.GraphicsDevice.Viewport.Project(position, Projection, view, Matrix.Identity);
            return new Vector2(v.X, v.Y);
        }
    }
}
